#include <linux/init.h>
#include <linux/module.h>
#include <linux/usb/input.h>
#include <linux/hid.h>
#include <linux/slab.h>

struct private_data {
    struct usb_driver usb;
    struct usb_device *usb_device;
    struct input_dev *usb_input_dev;
    struct urb *urb;
    u16 transfer_buffer_size;
    u8 *transfer_buffer;
    u8 old[8];
    dma_addr_t dma;
};

static struct private_data g_usb_keyboard;

static const unsigned char usb_kbd_keycode[] = { 
          0,  0,  0,  0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, 
         50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44,  2,  3,  
          4,  5,  6,  7,  8,  9, 10, 11, 28,  1, 14, 15, 57, 12, 13, 26, 
         27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64, 
         65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
        105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, 
         72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
        191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
        115,114,  0,  0,  0,121,  0, 89, 93,124, 92, 94, 95,  0,  0,  0,  
        122,123, 90, 91, 85,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
         29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
        150,158,159,128,136,177,178,176,142,152,173,140
};

/* usb驱动特有的匹配方式：利用usb描述符内容来匹配，这样当usb设备热插拔时，会根据usb设备描述符，匹配一个最合适的usb驱动。
   并动态的加载probe和disconnect函数 */
static const struct usb_device_id usb_id_table[] = {
    {USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_KEYBOARD)},
    {},
};

static int rk3588_usb_keyboard_init_input_dev(struct usb_interface *intf,
    struct input_dev **dev)
{
    int i;
    int ret;
    *dev = input_allocate_device();
    if (!*dev) {
        dev_err(&intf->dev, "allocate input device failed\n");
        return -1;
    }
    set_bit(EV_KEY, (*dev)->evbit);
    set_bit(EV_REP, (*dev)->evbit);
    for (i = 0; i < ARRAY_SIZE(usb_kbd_keycode); ++i) {
        set_bit(usb_kbd_keycode[i], (*dev)->keybit);
    }
    ret = input_register_device(*dev);
    if (ret) {
        dev_err(&intf->dev, "register input device failed.ret=%d\n", ret);
        input_free_device(*dev);
        return -2;
    }
    return 0;
}

/* urb传输完成回调函数 */
void usb_complete_callback(struct urb *urb)
{
    struct private_data *kbd = urb->context;
    int i;
    for (i = 0; i < 8; i++) {
        input_report_key(kbd->usb_input_dev, usb_kbd_keycode[i + 224], (kbd->transfer_buffer[0] >> i) & 1);
    }        

    for (i = 2; i < 8; i++) {
        if (kbd->old[i] > 3 && memscan(kbd->transfer_buffer + 2, kbd->old[i], 6) == kbd->transfer_buffer + 8) {
            if (usb_kbd_keycode[kbd->old[i]]) {
                input_report_key(kbd->usb_input_dev, usb_kbd_keycode[kbd->old[i]], 0);
            } else {
                hid_info(urb->dev,
                    "Unknown key (scancode %#x) released.\n",
                    kbd->old[i]);
            }
        } 

        if (kbd->transfer_buffer[i] > 3 && memscan(kbd->old + 2, kbd->transfer_buffer[i], 6) == kbd->old + 8) {
            if (usb_kbd_keycode[kbd->transfer_buffer[i]]) {
                input_report_key(kbd->usb_input_dev, usb_kbd_keycode[kbd->transfer_buffer[i]], 1);
            } else {
                hid_info(urb->dev,
                    "Unknown key (scancode %#x) pressed.\n",
                    kbd->transfer_buffer[i]);
            }
        }
    }
    input_sync(kbd->usb_input_dev);
    memcpy(kbd->old, kbd->transfer_buffer, 8);
    // 每次中断发生都要重新提交
    i = usb_submit_urb(urb, GFP_ATOMIC);
    if (i) {
        hid_err(urb->dev, "can't resubmit.ret=%d\n", i);
    }
}

static int rk3588_usb_keyboard_probe(struct usb_interface *intf,
    const struct usb_device_id *id)
{
    int ret;
    struct usb_device *device = interface_to_usbdev(intf);
    struct usb_host_interface *interface = intf->cur_altsetting; /* host-side wrapper for one interface setting's parsed descriptors */
    struct usb_endpoint_descriptor *endpoint = &interface->endpoint[0].desc;
    int pipe = usb_rcvintpipe(device, endpoint->bEndpointAddress);
    ret = rk3588_usb_keyboard_init_input_dev(intf, &g_usb_keyboard.usb_input_dev);
    if (ret) {
        dev_err(&intf->dev, "rk3588_usb_keyboard_init_input_dev failed.ret=%d\n", ret);
        goto INPUT_ERR;
    }

    /* 分配urb内存 */
    g_usb_keyboard.urb = usb_alloc_urb(0, GFP_KERNEL);

    /* 分配传输缓冲区，大小为端点最大传输包大小 */
    /* GFP_ATOMIC : 在中断处理函数，底半部，tasklet，定时器处理函数，urb完成函数中，
       在调用者持有自旋锁或读写锁时，以及当驱动将current->state修改为TASK_RUNNING
       时，应使用此标志
       GFP_NOIO : 在存储设备的块IO和错误处理路径中，应使用此标志
       GFP_KERNEL : 如果没有任何理由使用GFP_ATOMIC和GFP_NOIO，应使用此标志 */    
    g_usb_keyboard.transfer_buffer = usb_alloc_coherent(device, endpoint->wMaxPacketSize,
        GFP_ATOMIC, &g_usb_keyboard.dma);
    if (!g_usb_keyboard.transfer_buffer) {
        dev_err(&intf->dev, "allocate usb transfer buffer failed\n");
        goto ALLOC_BUFFER_ERR;
    }
    g_usb_keyboard.transfer_buffer_size = endpoint->wMaxPacketSize;
    g_usb_keyboard.usb_device = device;

    /* 填充urb结构 */
    usb_fill_int_urb(g_usb_keyboard.urb, device, pipe, g_usb_keyboard.transfer_buffer, 
        endpoint->wMaxPacketSize, usb_complete_callback, &g_usb_keyboard, endpoint->bInterval);

    g_usb_keyboard.urb->transfer_dma = g_usb_keyboard.dma;
    g_usb_keyboard.urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* urb->transfer_dma valid on submit */

    /* 提交urb */
    ret = usb_submit_urb(g_usb_keyboard.urb, GFP_KERNEL);
    if (ret) {
        dev_err(&intf->dev, "submit urb failed.ret=%d\n", ret);
        goto SUBMIT_URB_ERR;
    }
    return 0;
SUBMIT_URB_ERR:
    usb_free_coherent(g_usb_keyboard.usb_device, g_usb_keyboard.transfer_buffer_size,
        g_usb_keyboard.transfer_buffer, g_usb_keyboard.dma);
    usb_free_urb(g_usb_keyboard.urb);
ALLOC_BUFFER_ERR:
    input_unregister_device(g_usb_keyboard.usb_input_dev);
INPUT_ERR:
    return ret;
}

static void rk3588_usb_keyboard_disconnect(struct usb_interface *intf)
{
    /* cancel a transfer request and wait for it to finish */
    usb_kill_urb(g_usb_keyboard.urb);
    /* 释放缓冲区内存函数 */
    usb_free_coherent(g_usb_keyboard.usb_device, g_usb_keyboard.transfer_buffer_size,
        g_usb_keyboard.transfer_buffer, g_usb_keyboard.dma);
    /* 释放urb内存 */
    usb_free_urb(g_usb_keyboard.urb);
    input_unregister_device(g_usb_keyboard.usb_input_dev);
}

static struct usb_driver g_usb = {
    .name = "usb_keyboard",
    .probe = rk3588_usb_keyboard_probe,
    .disconnect = rk3588_usb_keyboard_disconnect,    
    .id_table = usb_id_table,
};

static int __init rk3588_usb_keyboard_init(void)
{
    int ret;
    g_usb_keyboard.usb = g_usb;
    ret = usb_register(&g_usb_keyboard.usb);
    if (ret) {
        printk(KERN_ERR "[%s][%d]register usb driver failed\n", __func__, __LINE__);
        return ret;
    }
    return 0;
}

static void __exit rk3588_usb_keyboard_exit(void)
{
    usb_deregister(&g_usb_keyboard.usb);
}

module_init(rk3588_usb_keyboard_init);
module_exit(rk3588_usb_keyboard_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yujie Guo");
MODULE_DESCRIPTION("rk3588-usb-keyboard driver for Learning");
